﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using CompactMapper.Orm.Contracts;
using CompactMapper.Orm.Extensions;
using CompactMapper.Orm.UnitOfWork.Commands.Implementations;

namespace CompactMapper.Orm.UnitOfWork.Mappings
{
    internal class InsertMappings : CommandMappingsBase
    {
        public InsertMappings(TablesTree tablesTree)
            : base(tablesTree)
        {
        }

        protected override void CreateSpecificMappings(Tree<Table> tablesTree)
        {
            foreach (var child in tablesTree.Children)
            {
                if (child.Node.InsertCommand == null)
                {
                    child.Node.InsertCommand = new InsertCommand(child.Node);
                }
                CreateSpecificMappings(child);
            }
        }

        public void Run(SQLiteConnection connection, IEnumerable<IKeyedPersistent> toPersistCollection)
        {
            if (toPersistCollection.HasNoItems())
                return;

            var mapping = TablesTree.GetTreeFor(toPersistCollection.First().GetType());
            toPersistCollection.Apply(item => RunCommands(connection, mapping, item, String.Empty, null));
        }


        private static void RunCommands(SQLiteConnection connection, Tree<Table> tablesTree, IKeyedPersistent toPersist,
                                        String parentTableName, IKey parentKey)
        {
            if (toPersist == null)
                return;

            var continueWithDescendendts = tablesTree.Node.InsertCommand.Run(connection, toPersist, parentTableName, parentKey);

            if (continueWithDescendendts)
            {
                foreach (var child in tablesTree.Children)
                {
                    foreach (var descendant in GetDescendants(toPersist, tablesTree.Node, child))
                    {
                        RunCommands(connection, child, descendant, toPersist.GetType().Name, toPersist.Key);
                    }
                }
            }
        }

        private static IEnumerable<IKeyedPersistent> GetDescendants(IKeyedPersistent parentInstance, Table parentTable,
                                                                    Tree<Table> tablesTree)
        {
            var parentName = parentTable.Name;

            var child = tablesTree.Node;
            var relationship = child.GetRelationshipWith(parentName);
            if (relationship != Relationship.None)
            {
                var relation = parentTable.Meta.RelationsByTableName[child.Name];
                var descendants = relation.GetValue(parentInstance);

                if (relationship.IsMultiple)
                {
                    return ((IEnumerable)descendants).OfType<IKeyedPersistent>().ExpungeNulls().ToArray();
                }
                return descendants == null
                           ? Enumerable.Empty<IKeyedPersistent>()
                           : new[] { (IKeyedPersistent)descendants };
            }
            return Enumerable.Empty<IKeyedPersistent>();
        }
    }
}